#ifndef __SUBSYSTEM_BLOCK_BEHAVIOR_HPP__
#define __SUBSYSTEM_BLOCK_BEHAVIOR_HPP__

#include "Subsystems/Subsystem.hpp"
#include "Terrain/TerrainChunk.h"
#include "Game/TerrainRaycastResult.hpp"
#include "GameEnum.h"
#include "Game/IInventory.hpp"
#include "Game/BlockPlacementData.hpp"
#include "Game/BlockDropValue.hpp"
#include "Game/WorldItem.hpp"

namespace PocketSurvival
{
    struct ComponentMiner;
    struct ComponentPlayer;
    struct ComponentBody;

    class SubsystemBlockBehavior : public Subsystem
    {
    protected:
        std::vector<int32_t> m_handleBlocks;

    public:
        inline const std::vector<int32_t> &getHandleBlocks();
        inline void addHandleBlock(int32_t contents);

        inline virtual void onChunkInitialized(TerrainChunk *chunk);
        inline virtual void onChunkDiscarding(TerrainChunk *chunk);
        inline virtual void onBlockGenerated(int32_t value, int32_t x, int32_t y, int32_t z, bool isLoaded);
        inline virtual void onBlockAdded(int32_t value, int32_t oldValue, int32_t x, int32_t y, int32_t z);
        inline virtual void onBlockAdded(int32_t value, int32_t oldValue, int32_t x, int32_t y, int32_t z, ComponentMiner *miner);
        inline virtual void onBlockRemoved(int32_t value, int32_t newValue, int32_t x, int32_t y, int32_t z);
        inline virtual void onBlockModified(int32_t value, int32_t oldValue, int32_t x, int32_t y, int32_t z);
        inline virtual void onNeighborBlockChanged(int32_t x, int32_t y, int32_t z, int32_t neighborX, int32_t neighborY, int32_t neighborZ);

        inline virtual bool onUse(const Ray3 &ray, ComponentMiner *componentMiner);
        inline virtual bool onInteract(const TerrainRaycastResult &raycastResult, ComponentMiner *componentMiner);
        inline virtual bool onAim(const Ray3 &aim, ComponentMiner *componentMiner, AimState state);
        inline virtual bool onEditBlock(int32_t x, int32_t y, int32_t z, int32_t value, ComponentPlayer *componentPlayer);
        inline virtual bool onEditInventoryItem(IInventory *inventory, uint32_t slotIndex, ComponentPlayer *componentPlayer);

        inline virtual void onBlockPlaced(ComponentMiner *miner, int32_t x, int32_t y, int32_t z, BlockPlacementData &outPlacementData, int32_t value);
        inline virtual void onItemHarvested(int32_t x, int32_t y, int32_t z, int32_t blockValue, BlockDropValue &outDropValue, int32_t &outNewBlockValue);
        inline virtual void onCollide(const CellFace &cellFace, float velocity, ComponentBody *componentBody);
        inline virtual void onExplosion(int32_t value, int32_t x, int32_t y, int32_t z, float damage);
        inline virtual void onFiredAsProjectile(Projectile *projectile);

        inline virtual bool onHitAsProjectile(std::shared_ptr<CellFace> cellFace, ComponentBody *componentBody, WorldItem *worldItem);
        inline virtual void onHitByProjectile(const CellFace &cellFace, WorldItem *worldItem);
        inline virtual uint32_t getProcessInventoryItemCapacity(IInventory *inventory, uint32_t slotIndex, int32_t cellValue);
        inline virtual void processInventoryItem(IInventory *inventory, uint32_t slotIndex, int32_t cellValue, uint32_t count, uint32_t processCount, int32_t &outProcessedValue, uint32_t &outProcessedCount);
    };

    inline const std::vector<int32_t> &SubsystemBlockBehavior::getHandleBlocks()
    {
        return m_handleBlocks;
    }
    inline void SubsystemBlockBehavior::addHandleBlock(int32_t contents)
    {
        for (auto value : m_handleBlocks)
        {
            if (value == contents)
            {
                return;
            }
        }
        m_handleBlocks.push_back(contents);
    }

    inline void SubsystemBlockBehavior::onChunkInitialized(TerrainChunk *chunk) {}
    inline void SubsystemBlockBehavior::onChunkDiscarding(TerrainChunk *chunk) {}
    inline void SubsystemBlockBehavior::onBlockGenerated(int32_t value, int32_t x, int32_t y, int32_t z, bool isLoaded) {}
    inline void SubsystemBlockBehavior::onBlockAdded(int32_t value, int32_t oldValue, int32_t x, int32_t y, int32_t z) {}
    inline void SubsystemBlockBehavior::onBlockAdded(int32_t value, int32_t oldValue, int32_t x, int32_t y, int32_t z, ComponentMiner *miner) {}
    inline void SubsystemBlockBehavior::onBlockRemoved(int32_t value, int32_t newValue, int32_t x, int32_t y, int32_t z) {}
    inline void SubsystemBlockBehavior::onBlockModified(int32_t value, int32_t oldValue, int32_t x, int32_t y, int32_t z) {}
    inline void SubsystemBlockBehavior::onNeighborBlockChanged(int32_t x, int32_t y, int32_t z, int32_t neighborX, int32_t neighborY, int32_t neighborZ) {}

    inline bool SubsystemBlockBehavior::onUse(const Ray3 &ray, ComponentMiner *componentMiner) { return false; }
    inline bool SubsystemBlockBehavior::onInteract(const TerrainRaycastResult &raycastResult, ComponentMiner *componentMiner) { return false; }
    inline bool SubsystemBlockBehavior::onAim(const Ray3 &aim, ComponentMiner *componentMiner, AimState state) { return false; }
    inline bool SubsystemBlockBehavior::onEditBlock(int32_t x, int32_t y, int32_t z, int32_t value, ComponentPlayer *componentPlayer) { return false; }
    inline bool SubsystemBlockBehavior::onEditInventoryItem(IInventory *inventory, uint32_t slotIndex, ComponentPlayer *componentPlayer) { return false; }

    inline void SubsystemBlockBehavior::onBlockPlaced(ComponentMiner *miner, int32_t x, int32_t y, int32_t z, BlockPlacementData &outPlacementData, int32_t value) {}
    inline void SubsystemBlockBehavior::onItemHarvested(int32_t x, int32_t y, int32_t z, int32_t blockValue, BlockDropValue &outDropValue, int32_t &outNewBlockValue) {}
    inline void SubsystemBlockBehavior::onCollide(const CellFace &cellFace, float velocity, ComponentBody *componentBody) {}
    inline void SubsystemBlockBehavior::onExplosion(int32_t value, int32_t x, int32_t y, int32_t z, float damage) {}
    inline void SubsystemBlockBehavior::onFiredAsProjectile(Projectile *projectile) {}

    inline bool SubsystemBlockBehavior::onHitAsProjectile(std::shared_ptr<CellFace> cellFace, ComponentBody *componentBody, WorldItem *worldItem) { return false; }
    inline void SubsystemBlockBehavior::onHitByProjectile(const CellFace &cellFace, WorldItem *worldItem) {}
    inline uint32_t SubsystemBlockBehavior::getProcessInventoryItemCapacity(IInventory *inventory, uint32_t slotIndex, int32_t cellValue) { return 0; }
    inline void SubsystemBlockBehavior::processInventoryItem(IInventory *inventory, uint32_t slotIndex, int32_t cellValue, uint32_t count, uint32_t processCount, int32_t &outProcessedValue, uint32_t &outProcessedCount) {}

}

#endif